#pragma once

template<typename ContextT,typename EventT>
class ConditionBase
{
public:
	explicit ConditionBase(ContextT& context)
		:m_context(context)
	{
	}

	bool Check(EventT const& event)
	{
		return DoCheck(event);
	}

	ContextT& GetContext() const {return m_context;}
private:
	
	virtual bool DoCheck(EventT const& event) = 0;

	ContextT& m_context;
};

template<typename ContextT,typename EventT>
class Status
{
public:
	typedef ConditionBase<ContextT,EventT> ConditionT;
public:
	Status(std::string const& name, ContextT& context)
		:m_name(name),m_context(context)
	{
	}
	virtual ~Status() {};

	std::string const& GetName() const{return m_name;};
	ContextT& GetContext() const {return m_context;}

	void AddJoint(ConditionT* condition, Status* status)
	{
		m_joints.push_back(StatusJoint(condition,status));
	}

	Status* GetNextStatus(EventT const& event) const
	{
		for (size_t i = 0; i < m_joints.size();++i)
		{
			StatusJoint const& joint = m_joints[i];
			if (joint.Check(event))
			{
				return joint.GetDestination();
			}
		}
		return NULL;
	}

	void Execute(EventT const& event)
	{
		DoBefore();
		DoExecute(event);
		DoAfter();
	}

private:
	class StatusJoint
	{
	public:
		StatusJoint(ConditionT* condition, Status* destination)
			:m_condition(condition),m_destination(destination)
		{
		}
		bool Check(EventT const& event) const {return m_condition->Check(event);}

		Status* GetDestination() const {return m_destination;}

	private:
		ConditionT* m_condition;
		Status* m_destination;
	};
	virtual void DoBefore() {}
	virtual void DoAfter() {}
	virtual void DoExecute(EventT const& event) = 0; 

	std::string m_name;
	ContextT& m_context;

	std::vector<StatusJoint> m_joints;
};

template<typename ContextT,typename EventT>
class StatusMachine
{
public:
	typedef Status<ContextT,EventT> StatusT;
	typedef typename StatusT::ConditionT ConditionT;
public:
	StatusMachine(StatusT* startStatus)
		:m_startStatus(startStatus),m_currentStatus(startStatus)
	{
		EventT event;
		startStatus->Execute(event);
		AddStatus(startStatus);
	}

	~StatusMachine()
	{
		for (size_t index = 0 ; index < m_statuses.size(); ++index)
		{
			delete m_statuses[index];
		}
		for (size_t index = 0 ; index < m_conditions.size(); ++index)
		{
			delete m_conditions[index];
		}
	}

	StatusT* GetCurrentStatus() const {return m_currentStatus;}

	void OnEvent(EventT const& event)
	{
		StatusT* nextStatus = GetCurrentStatus()->GetNextStatus(event);
		if (nextStatus)
		{
			m_currentStatus = nextStatus;
			nextStatus->Execute(event);
		}
	}

	StatusT* AddStatus(StatusT* status)
	{
		m_statuses.push_back(status);
		return status;
	}
	ConditionT* AddCondition(ConditionT* condition)
	{
		m_conditions.push_back(condition);
		return condition;
	}
	std::vector<StatusT*> const& GetStatuses() const 
	{
		return m_statuses;
	}
	void AddAllJoint(ConditionT* condition,StatusT* toStatus)
	{
		std::vector<StatusT*>::const_iterator iter = m_statuses.begin();
		for (;iter != m_statuses.end();++iter)
		{
			StatusT* status = *iter;
			if (status != toStatus)
			{
				status->AddJoint(condition,toStatus);
			}
		}
	}
private:
	StatusT* m_startStatus;
	StatusT* m_currentStatus;

	std::vector<StatusT*> m_statuses;
	std::vector<ConditionT*> m_conditions;
};
